package com.zhss.microservice.server.node;

import com.zhss.microservice.server.node.network.*;
import com.zhss.microservice.server.replica.ReplicationManager;
import com.zhss.microservice.server.slot.SlotManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zhss.microservice.server.config.Configuration;

import java.io.File;

/**
 * 微服务平台的server节点
 */
public class ServerNode {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServerNode.class);

    /**
     * 启动server节点
     */
    public void start() {
        Configuration configuration = Configuration.getInstance();
        Boolean isControllerCandidate = configuration.isControllerCandidate();

        // 等待监听其他server节点的连接请求
        ServerNetworkManager serverNetworkManager = ServerNetworkManager.getInstance();
        serverNetworkManager.startServerConnectionListener();

        // 如果是controller候选节点
        if(isControllerCandidate) {
            // 主动连接配置文件里排在自己前面的Controller候选节点
            if(!serverNetworkManager.connectBeforeControllerCandidateServers()) {
                return;
            }
            // 等待跟所有其他的server节点都完成连接
            serverNetworkManager.waitAllControllerCandidatesConnected();
            serverNetworkManager.waitAllServerNodeConnected();
        }
        // 如果是普通master节点，就直接对所有的controller候选节点都进行连接
        else {
            serverNetworkManager.connectAllControllerCandidates();
        }

        // 启动server节点的消息接收组件
        ServerMessageReceiver serverMessageReceiver = ServerMessageReceiver.getInstance();
        serverMessageReceiver.start();

        // 判断自己是否为controller候选节点
        Boolean isController = false;
        Integer serverNodeRole = ServerNodeRole.COMMON_NODE;

        if(isControllerCandidate) {
            // 投票参加选举controller
            ControllerCandidate controllerCandidate = ControllerCandidate.getInstance();

            // 在这里，要做一个判断，如果说集群里已经有controller节点了，此时就不需要重新发起选举了
            RemoteServerNodeManager remoteServerNodeManager = RemoteServerNodeManager.getInstance();
            if(remoteServerNodeManager.hasController()) {
                RemoteServerNode controller = remoteServerNodeManager.getController();
                // 发送一个请求给controller，让controller给自己同步一下对应的数据
                controllerCandidate.requestSlotsData(controller.getNodeId());
                controllerCandidate.waitForSlotsAllocation();
                controllerCandidate.waitForSlotsReplicaAllocation();
                controllerCandidate.waitReplicaNodeIds();
            } else {
                serverNodeRole = controllerCandidate.electController();
                LOGGER.info("通过选举得到自己的角色为：" + (serverNodeRole == ServerNodeRole.CONTROLLER ? "Controller" : "Controller候选节点"));

                // 根据自己的角色决定接下来要做什么事情
                if(serverNodeRole == ServerNodeRole.CONTROLLER) {
                    isController = true;
                    Controller controller = Controller.getInstance();
                    controller.allocateSlots();
                    controller.initControllerNode();
                    controller.sendControllerNodeId();
                } else if(serverNodeRole == ServerNodeRole.CANDIDATE) {
                    controllerCandidate.waitForSlotsAllocation();
                    controllerCandidate.waitForSlotsReplicaAllocation();
                    controllerCandidate.waitReplicaNodeIds();
                }
            }
        }

        // 只要你不是controller，所有的master节点都要在这里等待
        // 等待接收controller分配告诉你的槽位范围
        // 然后最终要做槽位数据的初始化
        if(!isController) {
            SlotManager slotManager = SlotManager.getInstance();
            slotManager.initSlots(null);
            slotManager.initSlotsReplicas(null, false);
            slotManager.initReplicaNodeId(null);
            ControllerNode.setNodeId(serverMessageReceiver.takeControllerNodeId());
        }

        // 如果是重新加入集群的话

        // 申请controller发起数据rebalance，把一些数据均匀的分配给他
        // rebalance的算法，可以做的很复杂，也可以做的比较简单一些
        // 你要是加入一些新的节点这样子的话，按理来说应该按照他之前的那种槽位计算的方式，重新计算一遍
        // 重新计算副本节点id，重新计算槽位副本
        // 必然会保证非常的均匀和均衡，就需要把一些slots开始来做转移，每个slots里都有自己的数据
        // 副本得重新进行初始化
        // 就会导致集群里有大量的数据在进行移动，各个节点之间突然可能会出现大量的数据在转移
        // 第一个版本的rebalance的算法，可以做的简单一些
        // 你发起一个请求给controller，让controller给你适当的分配一些slots
        // slots副本也可以重新进行一分配

        // 把一个叫做雏形的版本给他先实现一下
        // 我们应该是说让我们新加入集群的节点主动发送请求给controller去做rebalance
        // 让controller主动感知到集群里新加入了节点，自动去做rebalance


        ServerNodeRole.setRole(serverNodeRole);

        // 启动副本复制组件
        ReplicationManager replicationManager = ReplicationManager.getInstance();
        replicationManager.start();

        // 启动master节点的nio服务器
        ClientNIOServer clientNIOServer = ClientNIOServer.getInstance();
        clientNIOServer.start();
    }

}
